﻿namespace DanMuHelper.Tools
{

    using System.Collections.Generic;
    using DanMuHelper.Pool;
    using UnityEngine;
    using UnityEngine.SceneManagement;

    /// <summary>
    ///   UnityEngine类扩展
    ///      注意：TryGetComponent调用适用于2019.2版本以上
    /// </summary>
    public static class UnityEngineExtensions
    {
        #region 组件快捷操作

        /// <summary>
        ///   返回全路径(例如parent/father/childName)
        /// </summary>
        public static string FullPath(this GameObject gameObject)
        {
            var route = new List<string>();
            route.Add(gameObject.name);
            var parent = gameObject.transform.parent;
            while (parent)
            {
                route.Add(parent.gameObject.name);
                parent = parent.parent;
            }
            route.Reverse();
            return string.Join("/", route);
        }

        /// <summary>
        ///   正则表达式查找
        /// </summary>
        public static Transform FindWithRegex(this Transform transform, string name)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        ///   正则表达式查找
        /// </summary>
        public static List<Transform> FindAllWithRegex(this Transform transform, string name, List<Transform> result)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        ///   条件查找
        /// </summary>
        public static Transform Find(this Transform transform, System.Predicate<Transform> test)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        ///   条件查找
        /// </summary>
        public static List<Transform> FindAll(this Transform transform, System.Predicate<Transform> test, List<Transform> result)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// 查找子节点上的T组件, 在子节点路径已知的情况下，避免使用GetComponentInChildren以提升性能
        /// </summary>
        public static T FindChildWithComponent<T>(this Transform transform, string childPath) where T : Component
        {
            var child = transform.Find(childPath);
            if (null != child)
            {
                return child.GetComponent<T>();
            }
            return null;
        }

        /// <summary>
        /// 查找子节点上的T组件, 在子节点路径已知的情况下，避免使用GetComponentInChildren以提升性能
        /// </summary>
        public static T FindChildWithComponent<T>(this GameObject go, string childPath) where T : Component
        {
            return go.transform.FindChildWithComponent<T>(childPath);
        }

        /// <summary>
        /// 查找子节点上的T组件, 在子节点路径已知的情况下，避免使用GetComponentInChildren以提升性能
        /// </summary>
        public static T FindChildWithComponent<T>(this Component component, string childPath) where T : Component
        {
            var child = component.transform.Find(childPath);
            if (null != child)
            {
                return child.GetComponent<T>();
            }
            return null;
        }

        /// <summary>
        /// 如果不存在T组件，添加T
        /// </summary>
        /// <param name="gameObject"></param>
        /// <typeparam name="T"></typeparam>
        public static void TryAddComponent<T>(this GameObject gameObject) where T : Component
        {
            if (gameObject.TryGetComponent(out T c))
                return;
            gameObject.AddComponent<T>();
        }

        /// <summary>
        /// 如果不存在T组件，添加T
        /// </summary>
        /// <param name="gameObject"></param>
        /// <typeparam name="T"></typeparam>
        public static void TryAddComponent<T>(this Component component) where T : Component
        {
            component.gameObject.TryAddComponent<T>();
        }

        public static T GetOrAddComponent<T>(this GameObject gameObject) where T : Component
        {
            if (gameObject.TryGetComponent(out T c))
                return c;
            return gameObject.AddComponent<T>();
        }

        public static T GetOrAddComponent<T>(this Component component) where T : Component
        {
            return component.gameObject.GetOrAddComponent<T>();
        }

        public static T GetOrAddComponent<T>(this Transform transform) where T : Component
        {
            return transform.gameObject.GetOrAddComponent<T>();
        }

        /// <summary>
        /// 查找子节点，并执行GetOrAddComponent
        /// </summary>
        public static T GetOrAddChildComponent<T>(this Transform transform, string childPath) where T : Component
        {
            var c = transform.Find(childPath);
            if (null != c)
            {
                return c.GetOrAddComponent<T>();
            }
            return null;
        }

        public static Component GetOrAddComponent(this GameObject gameObject, System.Type ct)
        {
            if (gameObject.TryGetComponent(ct, out Component c))
            {
                return c;
            }
            return gameObject.AddComponent(ct);
        }

        public static void RemoveComponent<T>(this GameObject gameObject) where T : Behaviour
        {
            // Unity 2019.2 新api，避免在Editor中，查询不存在的Component时分配额外存储问题
            if (gameObject.TryGetComponent<T>(out T c))
            {
                c.enabled = false;
                Object.Destroy(c);
            }
        }

        public static void RemoveComponent<T>(this Component component) where T : Behaviour
        {
            // Unity 2019.2 新api，避免在Editor中，查询不存在的Component时分配额外存储问题
            if (component.TryGetComponent<T>(out T c))
            {
                c.enabled = false;
                Object.Destroy(c);
            }
        }

        public static void RemoveComponent<T>(this Transform transform) where T : Behaviour
        {
            // Unity 2019.2 新api，避免在Editor中，查询不存在的Component时分配额外存储问题
            if (transform.TryGetComponent<T>(out T c))
            {
                c.enabled = false;
                Object.Destroy(c);
            }
        }

        /// <summary>
        ///   移除所有T类型组件
        /// </summary>
        public static void RemoveComponents<T>(this Component component) where T : Behaviour
        {
            var components = new List<T>();
            component.GetComponents<T>(components);
            for (int i = components.Count - 1; i >= 0; i--)
            {
                components[i].enabled = false;
                Object.Destroy(components[i]);
            }
        }

        /// <summary>
        ///   移除所有T类型组件
        /// </summary>
        public static void RemoveComponents<T>(this GameObject gameObject) where T : Behaviour
        {
            var components = new List<T>();
            gameObject.GetComponents<T>(components);
            for (int i = components.Count - 1; i >= 0; i--)
            {
                components[i].enabled = false;
                Object.Destroy(components[i]);
            }
        }


        /// <summary>
        ///   删除所有子节点
        /// </summary>
        public static void DestroyChildren(this GameObject gameObject)
        {
            gameObject.transform.DestroyChildren();
        }

        /// <summary>
        ///   删除所有子节点
        /// </summary>
        public static void DestroyChildren(this Transform transform)
        {
            var childCount = transform.childCount;
            if (0 == childCount)
                return;
            var children = new GameObject[childCount];
            for (int i = childCount - 1; i >= 0; i--)
            {
                children[i] = transform.GetChild(i).gameObject;
                children[i].SetActive(false);
            }
            for (int i = childCount - 1; i >= 0; i--)
            {
                // Destroy不会立即生效，先设置父节点为null，避免在同一帧内再次获取到子节点
                children[i].transform.SetParent(null, false);
                GameObject.Destroy(children[i]);
            }
        }

        /// <summary>
        ///   删除所有子节点
        /// </summary>
        public static void DestroyChildrenImmediate(this Transform transform)
        {
            var childCount = transform.childCount;
            if (0 == childCount)
                return;
            var children = new GameObject[childCount];
            for (int i = childCount - 1; i >= 0; i--)
            {
                children[i] = transform.GetChild(i).gameObject;
                children[i].SetActive(false);
            }
            for (int i = childCount - 1; i >= 0; i--)
            {
                // Destroy不会立即生效，先设置父节点为null，避免在同一帧内再次获取到子节点
                children[i].transform.SetParent(null, false);
                GameObject.DestroyImmediate(children[i]);
            }
        }
        #endregion


        /// <summary>
        /// 修改节点Layer
        /// </summary>
        public static void ChangeLayer(this GameObject go, string layerName)
        {
            var layerId = LayerMask.NameToLayer(layerName);
            Stack<Transform> moveTargets = new Stack<Transform>();
            moveTargets.Push(go.transform);
            Transform curr;
            while (moveTargets.Count != 0)
            {
                curr = moveTargets.Pop();
                curr.gameObject.layer = layerId;
                foreach (Transform child in curr)
                    moveTargets.Push(child);
            }
        }

        #region RectTransform anchor 修改
        public enum AnchorPresets
        {
            TopLeft,
            TopCenter,
            TopRight,

            MiddleLeft,
            MiddleCenter,
            MiddleRight,

            BottomLeft,
            BottonCenter,
            BottomRight,
            BottomStretch,

            VertStretchLeft,
            VertStretchRight,
            VertStretchCenter,

            HorStretchTop,
            HorStretchMiddle,
            HorStretchBottom,

            StretchAll
        }

        /// <summary>
        /// 脚本修改RectTransform Anchor
        /// </summary>
        public static void SetAnchor(this RectTransform source, AnchorPresets allign)
        {
            Vector2 anchorMin = Vector2.zero;
            Vector2 anchorMax = Vector2.zero;
            switch (allign)
            {
                case (AnchorPresets.TopLeft):
                    {
                        anchorMin = new Vector2(0, 1);
                        anchorMax = new Vector2(0, 1);
                        break;
                    }
                case (AnchorPresets.TopCenter):
                    {
                        anchorMin = new Vector2(0.5f, 1);
                        anchorMax = new Vector2(0.5f, 1);
                        break;
                    }
                case (AnchorPresets.TopRight):
                    {
                        anchorMin = new Vector2(1, 1);
                        anchorMax = new Vector2(1, 1);
                        break;
                    }

                case (AnchorPresets.MiddleLeft):
                    {
                        anchorMin = new Vector2(0, 0.5f);
                        anchorMax = new Vector2(0, 0.5f);
                        break;
                    }
                case (AnchorPresets.MiddleCenter):
                    {
                        anchorMin = new Vector2(0.5f, 0.5f);
                        anchorMax = new Vector2(0.5f, 0.5f);
                        break;
                    }
                case (AnchorPresets.MiddleRight):
                    {
                        anchorMin = new Vector2(1, 0.5f);
                        anchorMax = new Vector2(1, 0.5f);
                        break;
                    }

                case (AnchorPresets.BottomLeft):
                    {
                        anchorMin = new Vector2(0, 0);
                        anchorMax = new Vector2(0, 0);
                        break;
                    }
                case (AnchorPresets.BottonCenter):
                    {
                        anchorMin = new Vector2(0.5f, 0);
                        anchorMax = new Vector2(0.5f, 0);
                        break;
                    }
                case (AnchorPresets.BottomRight):
                    {
                        anchorMin = new Vector2(1, 0);
                        anchorMax = new Vector2(1, 0);
                        break;
                    }

                case (AnchorPresets.HorStretchTop):
                    {
                        anchorMin = new Vector2(0, 1);
                        anchorMax = new Vector2(1, 1);
                        break;
                    }
                case (AnchorPresets.HorStretchMiddle):
                    {
                        anchorMin = new Vector2(0, 0.5f);
                        anchorMax = new Vector2(1, 0.5f);
                        break;
                    }
                case (AnchorPresets.HorStretchBottom):
                    {
                        anchorMin = new Vector2(0, 0);
                        anchorMax = new Vector2(1, 0);
                        break;
                    }

                case (AnchorPresets.VertStretchLeft):
                    {
                        anchorMin = new Vector2(0, 0);
                        anchorMax = new Vector2(0, 1);
                        break;
                    }
                case (AnchorPresets.VertStretchCenter):
                    {
                        anchorMin = new Vector2(0.5f, 0);
                        anchorMax = new Vector2(0.5f, 1);
                        break;
                    }
                case (AnchorPresets.VertStretchRight):
                    {
                        anchorMin = new Vector2(1, 0);
                        anchorMax = new Vector2(1, 1);
                        break;
                    }

                case (AnchorPresets.StretchAll):
                    {
                        anchorMin = new Vector2(0, 0);
                        anchorMax = new Vector2(1, 1);
                        break;
                    }
            }
            source.anchorMin = anchorMin;
            source.anchorMax = anchorMax;
        }

        #endregion RectTransform anchor 修改

        #region RectTransform pivot 修改

        public enum PivotPresets
        {
            TopLeft,
            TopCenter,
            TopRight,

            MiddleLeft,
            MiddleCenter,
            MiddleRight,

            BottomLeft,
            BottomCenter,
            BottomRight,
        }

        public static void SetPivot(this RectTransform source, PivotPresets preset)
        {
            switch (preset)
            {
                case (PivotPresets.TopLeft):
                    {
                        source.pivot = new Vector2(0, 1);
                        break;
                    }
                case (PivotPresets.TopCenter):
                    {
                        source.pivot = new Vector2(0.5f, 1);
                        break;
                    }
                case (PivotPresets.TopRight):
                    {
                        source.pivot = new Vector2(1, 1);
                        break;
                    }

                case (PivotPresets.MiddleLeft):
                    {
                        source.pivot = new Vector2(0, 0.5f);
                        break;
                    }
                case (PivotPresets.MiddleCenter):
                    {
                        source.pivot = new Vector2(0.5f, 0.5f);
                        break;
                    }
                case (PivotPresets.MiddleRight):
                    {
                        source.pivot = new Vector2(1, 0.5f);
                        break;
                    }

                case (PivotPresets.BottomLeft):
                    {
                        source.pivot = new Vector2(0, 0);
                        break;
                    }
                case (PivotPresets.BottomCenter):
                    {
                        source.pivot = new Vector2(0.5f, 0);
                        break;
                    }
                case (PivotPresets.BottomRight):
                    {
                        source.pivot = new Vector2(1, 0);
                        break;
                    }
            }
        }

        #endregion RectTransform pivot 修改

        /// <summary>
        /// 根据绝对路径删除场景中的go
        /// </summary>
        public static void DestroyGameObjectInScene(Scene scene, string path)
        {
            if (null == scene)
                return;
            var target = scene.FindChild(path);
            if (target)
            {
                UnityEngine.Object.Destroy(target.gameObject);
            }
        }

        /// <summary>
        /// 根据绝对路径删除当前场景中的go
        /// </summary>
        public static void DestroyGameObjectInScene(string path)
        {
            var scene = SceneManager.GetActiveScene();
            DestroyGameObjectInScene(scene, path);
        }

        /// <summary>
        /// 搜索场景中的子节点
        /// </summary>
        public static Transform FindChild(this Scene scene, string path)
        {
            if (null == path)
                return null;
            var pathLength = path.Length;
            if (0 == pathLength)
                return null;
            if (path[0] == '/')
            {
                path = path.Substring(1);
                pathLength--;
            }
            Transform result = null;
            string rootGameObjectName = null;
            string childPath = null;
            var pos = path.IndexOf('/', 1);
            if (pos >= 0)
            {
                rootGameObjectName = path.Substring(0, pos);
                if (pathLength > pos)
                {
                    childPath = path.Substring(pos + 1);
                }
            }
            else
            {
                rootGameObjectName = path;
            }
            var rootGameObjects = ListPool<GameObject>.Create();
            scene.GetRootGameObjects(rootGameObjects);
            var rootGameObjectCount = rootGameObjects.Count;
            for (int i = 0; i < rootGameObjectCount; i++)
            {
                if (string.Equals(rootGameObjects[i].name, rootGameObjectName))
                {
                    if (null == childPath)
                    {
                        result = rootGameObjects[i].transform;
                        break;
                    }
                    var child = rootGameObjects[i].transform.Find(childPath);
                    if (null != child)
                    {
                        result = child;
                        break;
                    }
                }
            }
            ListPool<GameObject>.Release(ref rootGameObjects);
            return result;
        }

        /// <summary>
        /// 增强版Find，可以从场景根路径查找
        /// </summary>
        public static Transform FindEx(this Transform transform, string path)
        {
            var pathLength = path.Length;
            if (path.StartsWith("/", System.StringComparison.Ordinal) && pathLength > 1)
            {
                return transform.gameObject.scene.FindChild(path.Substring(1));
            }
            else
            {
                return transform.Find(path);
            }
        }

    }

}
